package com.irdstudio.efp.flow.executor;

import java.util.List;
import java.util.Objects;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

import com.irdstudio.efp.flow.common.constance.FlowConstance;
import com.irdstudio.efp.flow.common.service.Executor;
import com.irdstudio.efp.flow.common.util.EngineUtil;
import com.irdstudio.efp.flow.common.vo.AprvUserVO;
import com.irdstudio.efp.flow.common.vo.EngineVO;
import com.irdstudio.efp.flow.common.vo.PageApproveVO;
import com.irdstudio.efp.flow.ext.AprvUserCalc;
import com.irdstudio.efp.flow.service.facade.BizFlowNodeService;
import com.irdstudio.efp.flow.service.facade.InstFlowParamService;
import com.irdstudio.efp.flow.service.facade.InstFlowProcessService;
import com.irdstudio.efp.flow.service.facade.InstFlowTaskService;
import com.irdstudio.efp.flow.service.vo.InstFlowParamVO;
import com.irdstudio.efp.flow.service.vo.InstFlowProcessVO;
import com.irdstudio.efp.flow.service.vo.InstFlowTaskVO;
import com.irdstudio.basic.framework.core.constant.FlowExceptionType;
import com.irdstudio.basic.framework.core.exception.FlowException;
import com.irdstudio.basic.framework.core.exception.TerminateException;
import com.irdstudio.basic.framework.core.util.StringUtil;
import com.irdstudio.basic.framework.core.util.TimeUtil;
import com.irdstudio.basic.framework.core.vo.PluginVO;

/**
 * 
 * 节点执行器
 * 
 * @author Cytus_
 * @since 2018年5月7日 下午6:29:37
 * @version 1.0
 *
 */
@Service("nodeExecutor")
public class NodeExecutor implements Executor {
	
	private static Logger logger = LoggerFactory.getLogger(NodeExecutor.class);
	
	@Autowired
	@Qualifier("bizFlowNodeService")
	private BizFlowNodeService bizFlowNodeService;
	
	@Autowired
	@Qualifier("instFlowProcessService")
	private InstFlowProcessService instFlowProcessService;
	
	@Autowired
	@Qualifier("instFlowTaskService")
	private InstFlowTaskService instFlowTaskService;
	
	@Autowired
	@Qualifier("instFlowParamService")
	private InstFlowParamService instFlowParamService;
	
	@Autowired
	@Qualifier("taskExecutor")
	private Executor taskExecutor;
	
	@Autowired
	@Qualifier("aprvUserCalc")
	private AprvUserCalc aprvUserCalc;
	
	
	public EngineVO execute(EngineVO evo) throws FlowException {
		
	    InstFlowProcessVO vo = instFlowProcessService.queryByNodeInstanceId(evo.getNodeInstanceId());
	    if (Objects.isNull(vo)) {
	        logger.error("当前节点实例:"+ evo.getNodeInstanceId() +", 业务节点："+ evo.getNodeId() +", 未实例化!");
	        throw new FlowException("当前节点实例:"+ evo.getNodeInstanceId() +", 业务节点："+ evo.getNodeId() +", 未实例化!");
	    } else if (FlowConstance.NODE_STATUS_END.equals(vo.getInstNodeState()) || FlowConstance.NODE_STATUS_TERMINATE.equals(vo.getInstNodeState())) {
	        logger.error("节点实例:"+ evo.getNodeInstanceId() +", 当前节点已终止!");
	        throw new FlowException("节点实例:"+ evo.getNodeInstanceId() +", 当前节点已终止!");
	    }
	    
	    evo.setNodeExecuteStatus(FlowConstance.NODE_STATUS_RUNNING);
	    updateFLowProcessStatus(evo.getNodeInstanceId(), evo.getNodeExecuteStatus());
		
		try {
			evo = getPluginVoInfos(evo);
		} catch (Exception e) {
			logger.error("当前节点实例:"+ evo.getNodeInstanceId() +", 业务节点："+ evo.getNodeId() +", 上下文数据信息提取出现异常!", e);
			throw new FlowException(e.getMessage(), e);
		}
		
		
		/*
		 * 寻找当前节点下需要等待执行的任务
		 * 1：节点下任务未初始化，先出使话任务列表，在寻找节点下首先要执行的任务
		 * 2：节点下任务已经初始化话，寻找任务状态为暂停的任务
		 */
		InstFlowTaskVO task = findCurrWaitExeTask(evo.getNodeInstanceId(), evo.getNodeId());
		if (Objects.isNull(task)) {
			//throw new FlowException("当前节点实例:"+ evo.getNodeInstanceId() +", 业务节点："+ evo.getNodeId() +"未找到需要执行的节点下任务");
			logger.warn("当前节点实例:"+ evo.getNodeInstanceId() +", 业务节点："+ evo.getNodeId() +"未找到需要执行的节点下任务");
			evo.setNodeExecuteStatus(FlowConstance.NODE_STATUS_END);
			updateFLowProcessStatus(evo.getNodeInstanceId(), evo.getNodeExecuteStatus());
			return evo;
		} 
		
		logger.info("当前节点实例:"+ evo.getNodeInstanceId() +", 待执行任务为："+ task.getTaskId());
		
		try {
			
			//如果寻找的当前任务状态为暂停, 将序列化存储到数据库中的pluginVo对象反序列化出来
			//if (FlowConstance.TASK_STATUS_PAUSE.equals(task.getTaskState())) {
				
				/*
				 * 目前仅为更新节点审批状态，如出现根据审批状态判断业务走向的目前暂时实现或者需要根据路由规则实现，如需要在此处实现，则需要进行代码扩展
				 */
				//updateNodeDealComment((PageApproveVO) evo.getExtParams().get(FlowConstance.PAGE_PLUGIN_VO_KEY), evo.getNodeInstanceId());
			//}
		
			
			/*
			 * 循环执行当前节点下任务
			 * 1：任务状态为结束时，节点状态为结束，寻找下一待执行节点
			 * 2：任务状态为暂停/异常时，节点状态为暂停/异常。此时需要将当前evo对象中的pluginVo对象序列化进数据库中
			 * 3：任务状态为暂停，任务插件类型为页面插件时，获取处理人信息并更新进数据库当前节点处理人信息字段中
			 */
			while (true) {
				evo.setTaskExecuteStatus(task.getTaskState());
				evo.setTaskId(task.getTaskId());
				evo.setPreTaskId(task.getPreviousTaskId());
				logger.info("当前节点实例:"+ evo.getNodeInstanceId() +", 执行任务："+ evo.getTaskId() +"开始......");
				taskExecutor.execute(evo);
				logger.info("当前节点实例:"+ evo.getNodeInstanceId() +", 执行任务："+ evo.getTaskId() +"结束, 任务状态为:"+ evo.getTaskExecuteStatus());
				if (FlowConstance.TASK_STATUS_PAUSE.equals(evo.getTaskExecuteStatus()) || FlowConstance.TASK_STATUS_EXCEPTION.equals(evo.getTaskExecuteStatus())) {
					
					logger.info("当前节点实例:"+ evo.getNodeInstanceId() +", 执行任务："+ evo.getTaskId() +"状态为:"+ evo.getTaskExecuteStatus() +", 上下文数据信息保存开始......");
					//暂停
					evo.setNodeExecuteStatus(FlowConstance.TASK_STATUS_PAUSE.equals(evo.getTaskExecuteStatus()) ? FlowConstance.NODE_STATUS_PAUSE : FlowConstance.NODE_STATUS_EXCEPTION);
					
					savePluginVoInfos(evo);
					
					if (FlowConstance.TASK_STATUS_PAUSE.equals(evo.getTaskExecuteStatus()) && !FlowConstance.PLUGIN_TYPE_SERVICE.equals(task.getBizPluginType())) {
						AprvUserVO userVo = aprvUserCalc.aprvUserCalc(evo);
						if (Objects.isNull(userVo)) {
							logger.error("当前实例化节点"+ evo.getNodeInstanceId() +"获得的审批人信息为空!");
							throw new FlowException("当前实例化节点"+ evo.getNodeInstanceId() +"获得的审批人信息为空!");
						}
						this.updateAprvUserInfo(userVo, evo.getNodeInstanceId());
					}
					logger.info("当前节点实例:"+ evo.getNodeInstanceId() +"执行任务："+ evo.getTaskId() +"状态为:"+ evo.getTaskExecuteStatus() +", 上下文数据信息保存结束, 退出本次执行节点执行信息!");
					break;
				}
				
				InstFlowTaskVO nextTask = findTaskByPreTaskId(evo.getNodeInstanceId(), task.getTaskId());
				if (Objects.isNull(nextTask)) {
					logger.info("当前节点ID:"+ evo.getNodeId() +", 实例化流水:"+ evo.getNodeInstanceId() +"下无可执行任务,当前节点执行结束!");
					evo.setNodeExecuteStatus(FlowConstance.NODE_STATUS_END);
					break;
				}
				task = nextTask;
			}
		} catch(TerminateException e) {
			logger.error("当前节点ID:"+ evo.getNodeId() +", 实例化流水:"+ evo.getNodeInstanceId() +"执行出现终止提示, cause by:"+ e.getMessage());
			evo.setNodeExecuteStatus(FlowConstance.NODE_STATUS_TERMINATE);
			if (e.getFlowExceptionType().isHighLevel(FlowExceptionType.NODE)) {
			    throw e;
			}
		} catch (Exception e) {
			logger.error("当前节点ID:"+ evo.getNodeId() +", 实例化流水:"+ evo.getNodeInstanceId() +"执行异常!", e);
			evo.setNodeExecuteStatus(FlowConstance.NODE_STATUS_EXCEPTION);
		} finally {
			updateFLowProcessStatus(evo.getNodeInstanceId(), evo.getNodeExecuteStatus());
			//将系统自动处理的更新为系统默认用户及机构
			if (FlowConstance.TASK_STATUS_END.equals(evo.getTaskExecuteStatus()) && StringUtil.isEmpty(vo.getProcessUserId())) {
			    updateSystemDealInfo(evo.getNodeInstanceId());
            }
		}
		return evo;
	}
	
	/**
	 * 更新节点状态
	 * @param instNodeId
	 * @param nodeStatus
	 * @return
	 */
	protected int updateFLowProcessStatus(String instNodeId, String nodeStatus) {
		InstFlowProcessVO process = new InstFlowProcessVO();
		process.setInstNodeState(nodeStatus);
		process.setInstNodeId(instNodeId);
		if (FlowConstance.NODE_STATUS_END.equals(nodeStatus) || FlowConstance.NODE_STATUS_TERMINATE.equals(nodeStatus)) {
			process.setEndTime(TimeUtil.getCurrentDateTime());
			process.setProcessState(FlowConstance.PROCESS_STATE_FINISH);
		}
		int record = instFlowProcessService.updateNodeStatusByInstNodeId(process);
		logger.info("当前节点实例:"+ instNodeId +", 更新节点状态为："+ nodeStatus +"结束");
		return record;
	}
	
	/**
	 * 查询下一节点信息
	 * @param instNodeId
	 * @param preTaskId
	 * @return
	 */
	protected InstFlowTaskVO findTaskByPreTaskId(String instNodeId, String preTaskId) {
		InstFlowTaskVO vo = new InstFlowTaskVO();
		vo.setInstNodeId(instNodeId);
		vo.setPreviousTaskId(preTaskId);
		InstFlowTaskVO nextVo = instFlowTaskService.queryTaskByPreTaskAndInstId(vo);
		logger.info("当前节点实例:"+ instNodeId +", 前置任务ID："+ preTaskId +"查询当前任务ID为:"+ (Objects.nonNull(nextVo) ? nextVo.getTaskId() : ""));
		return nextVo;
	}
	
	/**
	 * 需要当前待执行节点信息
	 * @param instNodeid
	 * @param bizNodeId
	 * @return
	 */
	protected InstFlowTaskVO findCurrWaitExeTask(String instNodeid, String bizNodeId) {
		int count = instFlowTaskService.countTaskByNodeInstId(instNodeid);
		if (count < 1) {
			InstFlowTaskVO initTask = new InstFlowTaskVO();
			initTask.setBizNodeId(bizNodeId);
			initTask.setInstNodeId(instNodeid);
			initTask.setTaskState(FlowConstance.TASK_STATUS_READY);
			instFlowTaskService.insertInitTaskByNodeId(initTask);
			return instFlowTaskService.queryFirstTaskByNodeInstId(instNodeid) ;
		} else {
			InstFlowTaskVO queryInfo = new InstFlowTaskVO();
			queryInfo.setInstNodeId(instNodeid);
			queryInfo.setTaskState(FlowConstance.TASK_STATUS_PAUSE +","+ FlowConstance.TASK_STATUS_EXCEPTION);
			//有查到多条报错风险
			InstFlowTaskVO vo = instFlowTaskService.queryTaskNodeIdAndStatus(queryInfo);
			
			if (Objects.isNull(vo)) {
				vo = getCurrNonExecuteTask(instNodeid); 
			}
			
			return vo;
		}
	}
	
	private InstFlowTaskVO getCurrNonExecuteTask(String instNodeId) {
		List<InstFlowTaskVO> vos = instFlowTaskService.queryByInstNodeId(instNodeId);
		InstFlowTaskVO vo = null;
		int countNonExe = (int) vos.parallelStream().filter(s -> FlowConstance.TASK_STATUS_READY.equals(s.getTaskState())).count();
		if (countNonExe > 0) {
			InstFlowTaskVO tempVo = vos.parallelStream().filter(s -> StringUtil.isEmpty(s.getPreviousTaskId())).findFirst().orElse(null);
			int i = 0, vosSize = vos.size();
			while (Objects.nonNull(tempVo) && !vos.isEmpty() && i < vosSize) {
				vos.remove(tempVo);
				if (FlowConstance.TASK_STATUS_READY.equals(tempVo.getTaskState())) {
					vo = tempVo;
					break;
				}
				String taskId = tempVo.getTaskId();
				tempVo = vos.parallelStream().filter(s -> taskId.equals(s.getPreviousTaskId())).findFirst().orElse(null);
			}
		}
		return vo;
	}
	
	/**
	 * 更新数据节点审批信息
	 * @param vo
	 * @param instNodeId
	 * @return
	 */
	protected int updateNodeDealComment(PageApproveVO vo, String instNodeId) {
		int record = 0;
		if (Objects.nonNull(vo)) {
			InstFlowProcessVO process = new InstFlowProcessVO();
			process.setInstNodeId(instNodeId);
			process.setApproveComment(vo.getAprvComment());
			process.setApproveOpinion(vo.getAprvOpinion());
			process.setProcessState(FlowConstance.PROCESS_STATE_FINISH);
			process.setApproveTime(TimeUtil.getCurrentDateTime());
			record = instFlowProcessService.updateApproveInfoByNodeInstId(process);
			logger.info("当前节点实例:"+ instNodeId +", 更新审批信息结束![审批结果:"+ vo.getAprvOpinion() +"]");
		}
		return record;
	}
	
	/**
	 * 系统处理的更新当前节点处理信息为系统默认信息
	 * @param instNodeId
	 * @return
	 */
	protected int updateSystemDealInfo(String instNodeId) {
	    InstFlowProcessVO process = new InstFlowProcessVO();
        process.setInstNodeId(instNodeId);
        process.setProcessOrgId(FlowConstance.SYSTEM_ORG_ID);
        process.setProcessOrgName(FlowConstance.SYSTEM_ORG_NAME);
        process.setProcessUserId(FlowConstance.SYSTEM_USER_ID);
        process.setProcessUserName(FlowConstance.SYSTEM_USER_NAME);
        process.setProcessState(FlowConstance.PROCESS_STATE_FINISH);
        return instFlowProcessService.updateAprvUserInfoByNodeInstId(process);
    }
	
	/**
	 * 更新审批人信息
	 * @param aprvUserVO
	 * @param instNodeId
	 * @return
	 */
	protected int updateAprvUserInfo(AprvUserVO aprvUserVO, String instNodeId) {
		InstFlowProcessVO process = new InstFlowProcessVO();
		process.setInstNodeId(instNodeId);
		if (FlowConstance.TASK_POOL_USER_ID.equals(aprvUserVO.getUserId())) {
		    process.setProcessOrgId(aprvUserVO.getOrgId() +"|"+ aprvUserVO.getLegalOrgId());
		} else {
		    process.setProcessOrgId(aprvUserVO.getOrgId());
		}
		process.setProcessOrgName(aprvUserVO.getOrgName());
		process.setProcessUserId(aprvUserVO.getUserId());
		process.setProcessUserName(aprvUserVO.getUserName());
		process.setProcessState(FlowConstance.PROCESS_STATE_WAIT);
		return instFlowProcessService.updateAprvUserInfoByNodeInstId(process);
	}
	
	/**
	 * 保存pluginvo信息
	 * @param evo
	 * @return
	 * @throws Exception
	 */
	protected int savePluginVoInfos(EngineVO evo) throws Exception {
		logger.error("当前节点ID:"+ evo.getNodeId() +", 实例化流水:"+ evo.getNodeInstanceId() +"序列化并保存相关参数信息开始......");
		String serializeStr = null;
		try {
			serializeStr = EngineUtil.serializePluginVo(evo.getPluginVo());
		} catch (Exception e) {
			logger.error("当前节点ID:"+ evo.getNodeId() +", 实例化流水:"+ evo.getNodeInstanceId() +"序列化相关参数异常!", e);
			throw new FlowException("当前节点ID:"+ evo.getNodeId() +", 实例化流水:"+ evo.getNodeInstanceId() +"序列化相关参数异常!", e);
		}
		
		InstFlowParamVO param = new InstFlowParamVO();
		param.setBizSerno(evo.getBizSerno());
		
		//先删除在新增
		instFlowParamService.deleteByPk(param);
		param.setInstNodeId(evo.getNodeInstanceId());
		param.setTaskId(evo.getTaskId());
		param.setBizParamContent(serializeStr);
		int record = instFlowParamService.insertInstFlowParam(param);
		logger.info("当前节点ID:"+ evo.getNodeId() +", 实例化流水:"+ evo.getNodeInstanceId() +"序列化并保存相关参数信息结束......");
		return record;
	}
	
	/**
	 * 
	 * 获取pluginVo对象
	 * 
	 * @param evo
	 * @return
	 * @throws Exception
	 */
	protected EngineVO getPluginVoInfos(EngineVO evo) throws Exception {				
		
		logger.info("当前节点实例:"+ evo.getNodeInstanceId() +", 待执行任务为："+ evo.getTaskId() +", 上下文数据信息提取开始......");
		InstFlowParamVO queryParam = new InstFlowParamVO();
		queryParam.setBizSerno(evo.getBizSerno());
		InstFlowParamVO param = instFlowParamService.queryContent(queryParam);
		if (Objects.nonNull(param) && StringUtil.isNotEmpty(param.getBizParamContent())) {
			String serializeStr = param.getBizParamContent();
			try {
				PluginVO vo = EngineUtil.deserializePluginVo(serializeStr);
				// modify by chenxuan 此处newVO会随着putPluginVO改变
				// PluginVO newVO = evo.getPluginVo();
				PluginVO newVO = new PluginVO();
				newVO = evo.getPluginVo();
				evo.getPluginVo().putPluginVO(vo);
				evo.getPluginVo().putPluginVO(newVO);
			} catch (Exception e) {
				logger.error("当前节点实例:"+ evo.getNodeInstanceId() +", 待执行任务为："+ evo.getTaskId() +", 上下文数据信息提取出现异常!", e);
				throw new FlowException("当前节点实例:"+ evo.getNodeInstanceId() +", 待执行任务为："+ evo.getTaskId() +", 上下文数据信息提取出现异常!", e);
			}
		}
		logger.info("当前节点实例:"+ evo.getNodeInstanceId() +", 待执行任务为："+ evo.getTaskId() +", 上下文数据信息提取结束......");
		return evo;
	}
}
